// KyPhoneUsbDrv.cpp : Defines the initialization routines for the DLL.
//

#include "stdafx.h"

#define EXP_FUN KYPHONEHIDDRV_API

#if defined(_DEBUG) && defined(_LIB__)
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
#include <dbt.h>

#include <stdlib.h>
#include <stdio.h>
#include "setupapi.h"

#include "KyPhoneHidDrv.h"
#include "UsbIo.h"
#include "Qunue.h"
#include "..\protocol\wjprotocol.h"

#define MAX_TX_FLY  2

DWORD  WINAPI thread_read_r832(LPVOID lpThreadParameter )
{
    MC_DEVICES * pDev = (MC_DEVICES *)lpThreadParameter;
	if( pDev == NULL ) return 0;

	pDev->bTaskRun = 1;

    HANDLE hWaitObject[3] ;
    hWaitObject[0] = pDev->OvRd.hEvent;
    hWaitObject[1] = g_hUsbDevMap.hEVTUsbExit;
    hWaitObject[2] = pDev->hEvtDemount;

    while(1)
    {   
        BYTE buff[10];
        DWORD TransBytes = 0;

        ResetEvent(pDev->OvRd.hEvent); 
        if(!ReadFile(pDev->hRead, buff, 9, &TransBytes, &pDev->OvRd))
        {
            DWORD error = GetLastError();
            if(error == ERROR_IO_PENDING)
            {
                DWORD dwEventid = WaitForMultipleObjects(3, hWaitObject, FALSE, INFINITE );
                if( dwEventid == WAIT_OBJECT_0 )
                {
                    if(!GetOverlappedResult(pDev->hRead, &pDev->OvRd, &TransBytes, TRUE))
                    {
                        break;
                    }
                    error = GetLastError();
                    if(error == ERROR_DEVICE_NOT_CONNECTED)
                    {
                        break;
                    }               
                }
                if( dwEventid >= WAIT_OBJECT_0 + 1 )
                {
                    break;
                }
            }
        }        

        DWORD dwEventid = WaitForMultipleObjects(2, &hWaitObject[1], FALSE, 0);
        if( dwEventid == WAIT_OBJECT_0 || dwEventid == (WAIT_OBJECT_0+1) )
        {
            break;
        }
        if( TransBytes )
        {
            INT len = buff[1]&0Xf;
            //if( len )
            {
                if( buff[1]&0X80 )
                {
                    pDev->usbBlock=0;
                }
                
                if ( 1 )
                {
                    char sztrace[1024*6] ={""};
                    char * ptrace = sztrace;
                    sprintf( ptrace, "R:(%02X)", buff[1]);
                    ptrace += strlen(ptrace);

                    for(int i=0; i<len;i++) 
                    {
                        sprintf( ptrace, "%X ,",buff[2+i]);
                        ptrace += strlen(ptrace);
                    }
                    sprintf( ptrace, "\n");
                    MyTrace(sztrace);
                }
                PostToQunue( pDev->hInBuff,&buff[2],len);
                g_hUsbDevMap.pCallbackDec(GetHandle(pDev));
            }
            continue;
        }
        else
        {
            SuspecDevicePlugOut();
        }

    }
    CancelIo(pDev->hRead);
	pDev->bTaskRun = 0;
    MyTrace(">>>>>> Usb Read Thead (832) End!");
    return 0;
}



/*****************************************************
*  Write function,  IOCTL_USB_GET_NODE_CONNECTION_INFORMATION 
*****************************************************/
static BOOL write_device_r832(HANDLE hDevice, USHORT OutputReportLen,BYTE *Output)
{
    MC_DEVICES * pDev = (MC_DEVICES*)hDevice;   
    BYTE OutputReport[255] = {0,1,2,3,4,5,6,7,8}; //new BYTE[OutputReportLen+15];
    bool found =false;
    DWORD TransBytes;
    OutputReport[0] = 3;
    OutputReport[1] = (BYTE)OutputReportLen;
    memcpy(&OutputReport[2], (BYTE *)Output, OutputReportLen);
   // PostToQunue( pDev->hOutBuff,OutputReport,8);

    
    ResetEvent(pDev->OvWr.hEvent); 
    if( !WriteFile( pDev->hWrite, OutputReport, 8, &TransBytes, &pDev->OvWr))
    {
        int err = GetLastError();
        if(err == ERROR_IO_PENDING)
        {
            SetLastError( APP_DEV_ERR(0) );
            HANDLE hWaitObject[2] ;
            hWaitObject[0] = pDev->OvWr.hEvent;
            hWaitObject[1] = g_hUsbDevMap.hEVTUsbExit;
            DWORD dwEventid = WaitForMultipleObjects(2, hWaitObject, FALSE, 1000 );
            if( dwEventid == WAIT_OBJECT_0 )
            {
                if(!GetOverlappedResult(pDev->hWrite, &pDev->OvWr, &TransBytes, TRUE))
                {
                    SetLastError( APP_DEV_ERR(1) );
                    return false;
                }
                if(GetLastError() == ERROR_DEVICE_NOT_CONNECTED)
                {
                    return false;
                }               
            }
            else if( dwEventid == WAIT_OBJECT_0 + 1 )
            {
                SetLastError( APP_DEV_ERR(2) );
                return false;
            }
            else
            {
                ::CancelIo(pDev->hWrite);
                SetLastError( APP_DEV_ERR(3) );
                return false;
            }
            
        }
    }
    return TRUE;
}


#define TRACE_FRAME_BYTES 32
int g_gdb_max_len = 0;
extern int TraceEnable();

INT ReadUSBDev_832(HANDLE hDevice, void * pbuf, int dwSize,\
               int timeout,DWORD * pErr)
{
    //return (INT)read_device(HANDLE hCom, USHORT InputReportLen)
    MC_DEVICES * pDev = GetUsbDviceHandle(hDevice); 

    BYTE * ptr = (BYTE *)pbuf;

    
    if( pDev == NULL ){ Sleep(timeout);  return 0;}
    int len; 
    do{
        len = GetQunueUsed(pDev->hInBuff);
        if( len )
        {
            if( g_gdb_max_len < len ) g_gdb_max_len = len;

            if( len > 0x20 )
            {
                MyTrace("Package too long, %d (max %d)Bytes\n",len, g_gdb_max_len);
                PurgeQunue(pDev->hInBuff);
            }

            if( dwSize > len ) dwSize = len;
            len = ::PeekFromQunue( pDev->hInBuff,ptr,dwSize);
            
            if ( TraceEnable() )
            {
                char sztrace[512] ={""};
                char * ptrace = sztrace;
                sprintf( ptrace, "R:[%02X]\t", hDevice);
                ptrace += strlen(ptrace);

                for(int i=0; i<len;i+=TRACE_FRAME_BYTES) 
                {
                    int frameb = len - i;
                    if (frameb > TRACE_FRAME_BYTES ) frameb = TRACE_FRAME_BYTES;
                    for( int j=0;j<frameb;j++)
                    {
                        sprintf( ptrace, "%02X,",*((BYTE*)(ptr+(i+j))));
                        ptrace += strlen(ptrace);
                    }
                    if( i<len-frameb )
                        strcpy( ptrace, "->\n");
                    else
                        strcpy( ptrace, "\n");
                    MyTrace(sztrace);
                    ptrace = sztrace;
                }
            }


            break;
        }
        Sleep(20);
        timeout -= 10;
    }while( timeout < 0);


    return len;
}


DWORD g_LastWriteTick;
INT WriteUSBDev_r832(HANDLE hDevice , void* pvbuf, int dwSize, DWORD * err)
{
    //Phrase the package and send diffrent package at diffrent output time
    DWORD ticks = ::GetTickCount();
    if(ticks - g_LastWriteTick < 50)
        Sleep(50);

    BYTE* pbuf = (BYTE* )pvbuf;
    int nWriteLen = dwSize;
    MC_DEVICES * pDev = GetUsbDviceHandle(hDevice);

    if ( TraceEnable() )
    {
        BYTE * ptr = (BYTE *) pvbuf;
        char sztrace[4096] ={""};
        char * ptrace = sztrace;
        sprintf( ptrace, "T:[%02X]\t", (BYTE)hDevice);
        ptrace += strlen(ptrace);

        for(int i=0; i<dwSize;i++) 
        {
            sprintf(ptrace,"%02X,",pbuf[i]);
            ptrace += strlen(ptrace);
        }
        sprintf( ptrace, "\n");
        MyTrace(sztrace);
    }
    

    

    while(pDev != NULL )
    {
        int nSynchDelay = 0;
        BYTE * psrc = (BYTE *)pbuf;
        INT retry = MAX_TX_FLY;

        while( dwSize>0)
        {
            if( nSynchDelay++ == 1)
            {
                DelayUs(25000);
                nSynchDelay=0;
            }
            int len = dwSize > 6 ? 6 : dwSize;
            if( FALSE == write_device_r832(pDev, len, (BYTE *)pbuf) )
            {
                MyTrace("--**++**-- Write to USB Error, %XH" ,::GetLastError());
                if( --retry )
                {
                    Sleep(50);
                    continue;
                }
                else
                {                  
                    break;
                }
            }
            pbuf += len;
            dwSize -= len;

            pDev->usbBlock=1;
            retry = MAX_TX_FLY;
            
            int timeout = 20;
            while(dwSize>0)
            {
                Sleep(10);
                if( pDev->usbBlock ==0 || timeout-- == 0 )
                    break;
            }
        }

        g_LastWriteTick = ::GetTickCount();
        
        if( hDevice != BROADCAST_HANDLE ) 
            return (pbuf-(BYTE*)pvbuf);
        
        pDev = GetUsbDviceHandle(pDev);
        pbuf = (BYTE* )pvbuf;
        dwSize = nWriteLen;
    }
    return (pbuf-(BYTE*)pvbuf);
}


const HID_DRIVER drv_ht83a832 = {
    CHIP_HT82A832R,
    0xFF00, //# Some Micro$oft non-standard extensions
    0,
    G_PID_82R832,
    G_VID_HOTEL,
    thread_read_r832,
    ReadUSBDev_832,
    WriteUSBDev_r832,
};
